/** UTF-8 Encoding: Provided a UTF-8 Converator
  * 
  * Authors:	Wei Li (oldrev@gmail.com)
  *
  * Date: June 3 2007
  * 
  *	Copyright: Copyright (C) 2007 Wei Li. All rights reserved.
  *
  * License: BSD
  *
  *
  */

module dotmars.text.utf8;

import dotmars.text.encoding;
import dotmars.base.stdtypes;
import dotmars.base.stdexcept;
import dotmars.text.utf;


class UTF8Encoding : Encoding
{
	const ubyte[3] UTF8Preamble = [0xEF, 0xBB, 0xBF];

	private bool m_unicodeMark;

	
	final class UTF8Encoder : Encoder
	{
		public override ubyte[] encode(string str, bool flush) {
			return cast(ubyte[]) str;
		}

		public override size_t encode(string str, Sink!(ubyte) sink, bool flush) {
			return internalEncode(str, sink);
		}
	}

	final class UTF8Decoder : Decoder
	{
		public override string decode(ubyte[] bytes, bool flush) {
			return cast(string) bytes;
		}

		public override size_t decode(ubyte[] bytes, Sink!(char) sink, bool flush) {
			return internalDecode(bytes, sink);
		}
	}

	public alias Encoding.decode decode;
	public alias Encoding.encode encode;

	public this(bool unicodeMark = false) {
		m_unicodeMark = unicodeMark;
	}

	public override Encoding clone(){
		auto e = new UTF8Encoding();
		e = this;
		return e;
	}
	
	public override string encodingName() { 
		return "UTF-8";
	}

	public override bool isSingleByte() {
		return false;
	}

	public override size_t getByteCount(string str) 
	in {
		assert(str !is null);
	}
	body {
		return str.length;
	}

	public override size_t getCharCount(ubyte[] bytes) 
	in {
		assert(bytes !is null);
	}
	body {

		string chars;

		//跳过 BOM
		if(bytes.length >= 3 && bytes[0..3] == UTF8Preamble[])
			chars = cast(string)bytes[3..$];
		else 
			chars = cast(string)bytes;

		return chars.length;
	}


	public override size_t getMaxByteCount(size_t charCount) {
		return charCount;
	}

	public override size_t getMaxCharCount(size_t byteCount) {
		return byteCount;
	}

	public override ubyte[] preamble() {
		return  m_unicodeMark ? UTF8Preamble : null;
	}

	public override size_t decode(ubyte[] bytes, Sink!(char) sink)
	in{
		assert(bytes !is null);
	}
	body {
		string chars;

		//跳过 BOM
		if(bytes.length >= 3 && bytes[0..3] == UTF8Preamble[])
			chars = cast(string)bytes[3..$];
		else 
			chars = cast(string)bytes;

		return internalDecode(bytes, sink);
	}

	public override size_t encode(string chars, Sink!(ubyte) sink)
	in{
		assert(chars !is null);
	}
	body {

		if(m_unicodeMark){
			foreach(ubyte b; UTF8Preamble)
				sink(b);
		}
		size_t count = internalEncode(chars, sink);
		return count + ( m_unicodeMark ? 3 : 0);
	}

	public override string decode(ubyte[] bytes) 
	in {
		assert(bytes !is null);
	}
	body {
		return cast(string) bytes;
	}

	public override ubyte[] encode(string str) 
	in {
		assert(str !is null);
	}
	body {
		return cast(ubyte[]) str;
	}

	private static size_t internalEncode(string str, Sink!(ubyte) sink)
	in{
		assert(str !is null);
		assert(sink !is null);
	}
	body {
		foreach(char c; str) {
			sink(cast(ubyte)c);
		}
		return str.length;
	}

	private static size_t internalDecode(ubyte[] bytes, Sink!(char) sink)
	in{
		assert(bytes !is null);
		assert(sink !is null);
	}
	body {
		foreach(ubyte c; bytes) {
			sink(cast(char)c);
		}
		return bytes.length;
	}
}

